

#include "CTrendChart.h"

CTrendChart::CTrendChart(QWidget *widget)
        : QWidget(widget)
          , plot(new QCustomPlot)
{
    InitWidget();
    InitUi();
    InitState();
    InitEvent();
}

void CTrendChart::InitUi()
{
    auto main_layout = new QVBoxLayout(this);
    main_layout->addWidget(plot,1);

    auto hbox = new QHBoxLayout();
    hbox->addLayout(legend_box,1);
    hbox->addStretch(1);
    hbox->addWidget(select_all_label);
    hbox->addWidget(select_all);

    main_layout->addLayout(hbox);
}

void CTrendChart::AddGraph(const QString &name,PosType pos/* = PosLeft*/)
{
    QCPGraph * graph = nullptr;
    switch (pos)
    {
        case PosLeft:
            graph = plot->addGraph(plot->xAxis,plot->yAxis);
            break;
        case PosRight:
            graph = plot->addGraph(plot->xAxis,plot->yAxis2);
            break;
    }
    assert(nullptr != graph);
    graph->setName(name);
    graph->setSelectable(QCP::stSingleData);
    all_graph.insert(name,graph);
    AddLegend(name);

    if(all_color.contains(name))
        SetGraphColor(name, all_color.value(name));
}

void CTrendChart::AddGraph(const QString &name, const QVector<double> &key, const QVector<double> &value, PosType pos/* = PosLeft*/)
{
    AddGraph(name, pos);
    SetData(name,key,value);
}

void CTrendChart::ClearGraph(const QString &name/* = ""*/)
{
    if(!name.isEmpty())
    {
        DelLegend(name);
        plot->removeGraph(all_graph.take(name));
        ClearTracer(name);
    }
    else
    {
        for(auto name : all_graph.keys())
        {
            DelLegend(name);
            plot->removeGraph(all_graph.take(name));
        }

        ClearTracer();
    }

    SetLineVisible(cursor_line,false);
    text_item->setVisible(false);
    plot->replot();
}

void CTrendChart::SetGraphVisible(const QString &name, bool visible)
{
    all_graph.value(name)->setVisible(visible);
    plot->replot();
}

void CTrendChart::SetData(const QString &name, const QVector<double> &key, const QVector<double> &value)
{
    auto graph = all_graph.value(name, nullptr);
    if(nullptr == graph)
        return;

    graph->setData(key,value);

    emit PlotFinished();
}

void CTrendChart::SetData(const TrendDataList &dataSet)
{
    for(auto graph : all_graph)
        graph->data()->clear();
    for (auto data : dataSet)
        AppendData(data->time,data->dataSet);

    emit PlotFinished();
}

void CTrendChart::AppendData(double key, const QString &name, double value)
{
    auto graph = all_graph.value(name, nullptr);
    if(nullptr == graph)
        return;

    graph->addData(key,value);
}

void CTrendChart::AppendData(double key, const QMap<QString, double> &values)
{
    for (auto name : values.keys())
        AppendData(key,name,values.value(name));
}

void CTrendChart::InitEvent()
{
    connect(select_all,&QCheckBox::stateChanged,this,[=](){
        auto check = select_all->checkState();
        if(check == Qt::Checked)
        {
            for (auto name: all_graph.keys())
            {
                SetGraphVisible(name, true);
                SetLegendColor(name,all_color.value(name));
            }
        }
        else if(check == Qt::Unchecked)
        {
            for (auto name: all_graph.keys())
            {
                SetGraphVisible(name, false);
                SetLegendColor(name,Qt::gray);
            }
        }
    });

    connect(plot,&QCustomPlot::plottableClick,this,[=](QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event){

        auto select_graph = all_graph.value(plottable->name(), nullptr);
        if(nullptr == select_graph)
            return ;

        int offset = select_graph->data()->size() - dataIndex;

        QPointF end_point(0,0);
        QString text;
        ClearTracer("");
        for(auto name : all_graph.keys()){
            auto graph = all_graph.value(name);
            if(!graph->visible())
                continue;

            auto size = graph->data()->size();
            int ret = size - offset;
            if(ret < 0)
                continue;


            auto data = graph->data()->at(ret);

            if(name == plottable->name())
                end_point = QPointF(data->key,data->value);

            text += QString("%1\t%2\n").arg(name,QString::number(data->value));

            AddTracer(name,data->key);
        }

        if(text.isEmpty()){
            SetLineVisible(cursor_line,false);
            text_item->setVisible(false);
            return;
        }

        SetLineVisible(cursor_line,true);
        text_item->setVisible(true);

        text_item->setText(text.trimmed());

        text_item->position->setParentAnchor(all_tracer.value(plottable->name())->position);
        text_item->position->setCoords(0.1,0.1);
        UpdateLinePos(cursor_line,end_point.x());

        plot->replot();
    });
}

void CTrendChart::InitWidget()
{
    legend_box = new QHBoxLayout;
    select_all = new QCheckBox;
    select_all_label = new QLabel("全选");
    cursor_line = AddLine();

    text_item = new QCPItemText(plot);
}

void CTrendChart::InitState()
{
    timeTicker = QSharedPointer<QCPAxisTickerTime>(new QCPAxisTickerTime);
    timeTicker->setTimeFormat("%h:%m:%s");
    timeTicker->setTickStepStrategy(QCPAxisTickerTime::tssMeetTickCount);
    timeTicker->setTickCount(xTickCount);
    plot->xAxis->setTicker(timeTicker);

    plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables | QCP::iMultiSelect/* | QCP::iSelectItems*/);
    plot->axisRect()->setupFullAxesBox();

    plot->axisRect()->setRangeDragAxes({plot->xAxis}, {plot->yAxis, plot->yAxis2});
    plot->axisRect()->setRangeZoomAxes({plot->xAxis}, {plot->yAxis, plot->yAxis2});

    select_all->setTristate(true);
    select_all->setChecked(true);

    select_all_label->setVisible(false);
    select_all->setVisible(false);

    text_item->position->setType(QCPItemPosition::ptAxisRectRatio);
    text_item->setFont(QFont(font().family(), 16));
    text_item->setPen(QPen(Qt::black));
    text_item->setPadding(QMargins(5,5,5,5));
    text_item->setVisible(false);
}

void CTrendChart::SetGraphColor(const QString &name, const QColor &color)
{
    auto graph = all_graph.value(name, nullptr);
    if(nullptr == graph)
        return;

    QPen graphPen;
    graphPen.setColor(color);

    graph->setPen(graphPen);
    plot->replot();
}

void CTrendChart::SetColor(const QMap<QString, QColor> &color)
{
    all_color = color;
}

void CTrendChart::SetXRangeStart(double start, double size_s)
{
    plot->xAxis->setRange(start, size_s, Qt::AlignRight);
    plot->replot();
}

void CTrendChart::SetXRangeEnd(double end, double size_s)
{
    plot->xAxis->setRange(end - size_s, size_s, Qt::AlignRight);
    plot->replot();
}

void CTrendChart::SetYRange(double min1, double max1, double min2, double max2)
{
    plot->yAxis->setRange(QCPRange(min1,max1));

    if(!enableMultiGroup)
        return;

    plot->yAxis2->setRange(QCPRange(min2,max2));
    plot->replot();
}

void CTrendChart::SetXLabel(const QString &label)
{
    plot->xAxis->setLabel(label);
    plot->replot();
}

void CTrendChart::SetYLabel(const QString &label1, const QString &label2)
{
    plot->yAxis->setLabel(label1);

    if(!enableMultiGroup)
        return;

    plot->yAxis2->setLabel(label2);
    plot->yAxis2->setVisible(enableMultiGroup);
    plot->yAxis2->setTickLabels(enableMultiGroup);
    plot->replot();
}

void CTrendChart::Update()
{
    plot->replot();
}

void CTrendChart::AdjustAxis()
{
    plot->rescaleAxes();
}

void CTrendChart::AddLegend(const QString &name)
{
    if(all_legend.contains(name))
        return;

    auto button = new QToolButton();
    button->setText(name);
    button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    all_legend.insert(name,button);
    legend_box->addWidget(button,0,Qt::AlignLeft);

    connect(button,&QToolButton::clicked,this,[=](){
        auto visible = all_graph.value(name)->visible();
        SetGraphVisible(name, !visible);

        SetLegendColor(name,!visible ? all_color.value(name) : Qt::gray);

        int visible_count = 0;
        for(auto graph : all_graph)
        {
            if(graph->visible())
                ++visible_count;
        }

        select_all->blockSignals(true);
        if(visible_count == all_graph.size())
            select_all->setCheckState(Qt::Checked);
        else if(visible_count == 0)
            select_all->setCheckState(Qt::Unchecked);
        else
            select_all->setCheckState(Qt::PartiallyChecked);
        select_all->blockSignals(false);
    });

    if(!all_color.contains(name))
        return;

    SetLegendColor(name,all_color.value(name));
    SwitchSelectState();
}

void CTrendChart::SwitchSelectState()
{
    select_all->setVisible(!all_legend.isEmpty());
    select_all_label->setVisible(!all_legend.isEmpty());
}

void CTrendChart::DelLegend(const QString &name)
{
    if(!all_legend.contains(name))
        return;

    auto wid = all_legend.take(name);

    legend_box->removeWidget(wid);
    delete wid;

    SwitchSelectState();
}

QString CTrendChart::AddLine(double value/* = 0*/, const QColor &color /*= Qt::black*/,Qt::Orientation orientation/* = Qt::Vertical*/, CTrendChart::PosType pos/* = PosLeft*/, bool intersect/* = false*/)
{
    auto line = new QCPItemStraightLine(plot);
    line->setPen(QPen(color,2,Qt::DashLine));
    QString name = QUuid::createUuid().toString();

    auto property = new LineProperty;
    property->name = name;
    property->value = value;
    property->color = color;
    property->orientation = orientation;
    property->pos = pos;
    property->intersect = intersect;

    all_line.insert(name,{line,property});

    UpdateLinePos(name, value);
    SetLineVisible(name,false);
    return name;
}

void CTrendChart::UpdateLinePos(const QString &name, double value)
{
    if(!all_line.contains(name))
        return;

    auto ret = all_line.value(name);
    switch (ret.second->pos)
    {
        case PosLeft:
        {
            ret.first->point1->setAxes(plot->xAxis, plot->yAxis);
            ret.first->point2->setAxes(plot->xAxis, plot->yAxis);
            break;
        }
        case PosRight:
            ret.first->point1->setAxes(plot->xAxis, plot->yAxis2);
            ret.first->point2->setAxes(plot->xAxis, plot->yAxis2);
            break;
    }

    switch (ret.second->orientation)
    {
        case Qt::Horizontal:
        {
            ret.first->point1->setCoords(0, value);
            ret.first->point2->setCoords(1, value);
            break;
        }
        case Qt::Vertical:
            ret.first->point1->setCoords(value, 0);
            ret.first->point2->setCoords(value, 1);
            break;
    }
    plot->replot();
}

void CTrendChart::SetLineVisible(const QString &name, bool visible)
{
    if(!all_line.contains(name))
        return;

    auto line = all_line.value(name);

    line.first->setVisible(visible);
}

void CTrendChart::RemoveLine(const QString &name/* = ""*/)
{
    if(!name.isEmpty())
    {
        plot->removeItem(all_line.take(name).first);
    }
    else
    {
        for(auto name : all_line.keys())
            plot->removeItem(all_line.take(name).first);
    }

    plot->replot();
}

void CTrendChart::SetLegendColor(const QString &name, const QColor &color)
{
    if(!all_legend.contains(name))
        return;

    QPixmap pixmap(64,64);
    pixmap.fill(color);
    all_legend.value(name)->setIcon(QIcon(pixmap));
    this->update();
}

void CTrendChart::AddTracer(const QString &name,double key)
{
    if(!all_graph.contains(name))
        return;

    auto tracer = new QCPItemTracer(plot);
    tracer->setGraph(all_graph.value(name));
    tracer->setGraphKey(key);
    tracer->setStyle(QCPItemTracer::tsCircle);
    tracer->setPen(QPen(all_color.value(name,Qt::gray)));
    tracer->setBrush(all_color.value(name,Qt::gray));
    tracer->setSize(7);

    all_tracer.insert(name, tracer);
}

void CTrendChart::ClearTracer(const QString &name/* = ""*/)
{
    if(!name.isEmpty())
    {
        plot->removeItem(all_tracer.take(name));
    }
    else
    {
        for(auto name : all_tracer.keys())
            plot->removeItem(all_tracer.take(name));
    }

    plot->replot();
}






